package tcp

import (
	"bufio"
	"context"
	"io"
	"net"
	"sync"
	"time"

	"goRedis/lib/logger"
	"goRedis/lib/sync/atomic"
	"goRedis/lib/sync/wait"
)

// EchoClient is client for EchoHandler, using for test
type EchoClient struct {
	waiting wait.Wait
	conn    net.Conn
}

// Close close connection
func (e *EchoClient) Close() error {
	//10s handle处理时间，等待处理完或超时 关闭连接
	e.waiting.WaitWithTimeout(10 * time.Second)
	e.conn.Close()
	return nil
}

// EchoHandler echos received line to client, using for test
type EchoHandler struct {
	activeConn sync.Map
	closing    atomic.Boolean
}

// MakeHandler creates EchoHandler
func MakeHandler() *EchoHandler {
	return &EchoHandler{}
}

// Handle echos received line to client
func (e *EchoHandler) Handle(ctx context.Context, conn net.Conn) {
	if e.closing.Get() {
		// closing handler refuse new connection,  reader.ReadString() will return io.EOF
		conn.Close()
	}
	client := &EchoClient{
		conn: conn,
	}
	e.activeConn.Store(client, struct{}{})

	reader := bufio.NewReader(conn)
	for {
		// may occurs: client EOF, client timeout, server early close
		msg, err := reader.ReadString('\n')
		if err != nil {
			if err == io.EOF {
				logger.Info("connection close")
				e.activeConn.Delete(client)
			} else {
				logger.Warn(err)
			}
			return
		}
		client.waiting.Add(1)
		conn.Write([]byte(msg))
		client.waiting.Done()
	}
}

// Close 方法一定要保证线程安全，sync.Map线程安全
// Close stops echo handler
func (e *EchoHandler) Close() error {
	logger.Info("handler shutting down...")
	e.closing.Set(true)
	e.activeConn.Range(func(key, value any) bool {
		key.(*EchoClient).Close()
		return true
	})
	return nil
}
